package mys

import (
	"crypto/md5"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/lintx/genshin-impact-helper-go/logger"
	"gitee.com/lintx/genshin-impact-helper-go/request"
	"gitee.com/lintx/genshin-impact-helper-go/util"
	"github.com/gofrs/uuid"
	"go.uber.org/zap"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type Mys struct {
	awards *SignAward
	header http.Header
}

type SignAward struct {
	Retcode int    `json:"retcode"`
	Message string `json:"message"`
	Data    struct {
		Month  int `json:"month"`
		Awards []struct {
			Icon string `json:"icon"`
			Name string `json:"name"`
			Cnt  int    `json:"cnt"`
		} `json:"awards"`
	} `json:"data"`
}

type BindingInfo struct {
	Retcode int    `json:"retcode"`
	Message string `json:"message"`
	Data    struct {
		List []Player `json:"list"`
	} `json:"data"`
}

type Player struct {
	GameBiz    string `json:"game_biz"`
	Region     string `json:"region"`
	GameUid    string `json:"game_uid"`
	Nickname   string `json:"nickname"`
	Level      int    `json:"level"`
	IsChosen   bool   `json:"is_chosen"`
	RegionName string `json:"region_name"`
	IsOfficial bool   `json:"is_official"`
}

type SignInfo struct {
	Retcode int    `json:"retcode"` //-100:未登录
	Message string `json:"message"` //尚未登录
	Data    struct {
		TotalSignDay int    `json:"total_sign_day"`
		Today        string `json:"today"`
		IsSign       bool   `json:"is_sign"`
		FirstBind    bool   `json:"first_bind"`
		IsSub        bool   `json:"is_sub"`
		MonthFirst   bool   `json:"month_first"`
	} `json:"data"`
}

type SignPost struct {
	ActId  string `json:"act_id"`
	Region string `json:"region"`
	Uid    string `json:"uid"`
}

type SignResult struct {
	RetCode int    `json:"ret_code"` //-401:请求异常,-5003:已经签到过了,0:OK
	Message string `json:"message"`  //
	Data    struct {
		Code string `json:"code"` //ok
	} `json:"data"`
}

var (
	//signInfoUrl string	//获取角色的签到信息，需要uid
	signUrl        = `https://api-takumi.mihoyo.com/event/bbs_sign_reward/sign` //签到，需要post
	appVersion     = `2.35.2`
	ua             = `Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) Apple WebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/` + appVersion
	actId          = `e202009291139501`
	acceptEncoding = `gzip, deflate, br`
	refererUrl     = `https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true&act_id=` + actId + `utm_source=bbs&utm_medium=mys&utm_campaign=icon` //http头中的referer字段

	bindingUrl  = `https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie?game_biz=hk4e_cn` //获取绑定的角色列表，需要cookie
	awardUrl    = `https://api-takumi.mihoyo.com/event/bbs_sign_reward/home?act_id=e202009291139501`    //获取奖励列表，无需cookie，可以直接访问
	signInfoUrl = `https://api-takumi.mihoyo.com/event/bbs_sign_reward/info?act_id=` + actId            //获取签到信息，还需要拼接region和uid
)

func New() *Mys {
	m := &Mys{}
	m.header = http.Header{}
	m.header.Set(`User-Agent`, ua)
	m.header.Set(`Referer`, refererUrl)
	m.header.Set(`Accept-Encoding`, acceptEncoding)
	m.getAward()
	return m
}

func (m *Mys) Sign(cookie string) {
	header := m.header.Clone()
	header.Set(`Cookie`, cookie)
	info, err := m.getBindingInfo(header)
	if err != nil {
		logger.Logger.Error(`get binding error`, zap.Error(err))
	} else {
		logger.Logger.Debug(`get binding info`, zap.Any(`info`, info))
		for _, p := range info.Data.List {
			//获取到了角色信息，开始签到
			signInfo, signResult, err := m.sign(p, header)
			if err != nil {
				field := []zap.Field{zap.Error(err)}
				if signInfo != nil {
					field = append(field, zap.Any(`sign info`, signInfo))
				}
				if signResult != nil {
					field = append(field, zap.Any(`sign result`, signResult))
				}
				logger.Logger.Error(`签到失败`, field...)
			} else {
				msg := p.Nickname + `签到成功,本月共签到` + strconv.Itoa(signInfo.Data.TotalSignDay+1) + `次`
				if m.awards != nil {
					award := m.awards.Data.Awards[signInfo.Data.TotalSignDay]
					msg += `,奖励:` + strconv.Itoa(award.Cnt) + award.Name
				}
				logger.Logger.Info(msg, zap.Any(`sign info`, signInfo), zap.Any(`sign result`, signResult))
			}
		}
	}
}

func (m *Mys) Signs(cookies []string) {
	for _, cookie := range cookies {
		m.Sign(cookie)
	}
}

func (m *Mys) sign(player Player, header http.Header) (*SignInfo, *SignResult, error) {
	info, err := m.signInfo(player, header)
	if err != nil {
		return nil, nil, err
	}
	if info.Data.IsSign {
		return nil, nil, errors.New(`今天已签到`)
	} else {
		data := SignPost{
			ActId:  actId,
			Region: player.Region,
			Uid:    player.GameUid,
		}
		d1, err := json.Marshal(data)
		if err != nil {
			return nil, nil, err
		} else {
			d2, err := request.Post(signUrl, newHeader(header), d1)
			if err != nil {
				return nil, nil, err
			} else {
				result := &SignResult{}
				err := json.Unmarshal(d2, result)
				if err != nil {
					return nil, nil, err
				} else {
					if result.RetCode == 0 {
						return info, result, nil
					} else {
						return nil, nil, errors.New(result.Message)
					}
				}
			}
		}
	}
}

func (m *Mys) signInfo(player Player, header http.Header) (*SignInfo, error) {
	url := signInfoUrl + "&region=" + player.Region + "&uid=" + player.GameUid
	//d, err := request.Get(url, newHeader(header))
	d, err := request.Get(url, header)
	if err != nil {
		return nil, err
	} else {
		info := &SignInfo{}
		err = json.Unmarshal(d, &info)
		if err != nil {
			return nil, err
		}
		if info == nil {
			return nil, errors.New(`未知错误，获取签到信息失败`)
		}
		if info.Retcode == -100 {
			return nil, errors.New(`COOKIE过期，请在米游社退出登录后重新登录，并设置最新的COOKIE`)
		}
		if info.Retcode != 0 {
			return nil, errors.New(info.Message)
		}
		return info, nil
	}
}

func newHeader(header http.Header) http.Header {
	header.Set(`x-rpc-device_id`, strings.ToUpper(strings.ReplaceAll(uuid.NewV3(uuid.NamespaceURL, header.Get(`Cookie`)).String(), `-`, ``)))
	header.Set(`x-rpc-client_type`, `5`)
	header.Set(`x-rpc-app_version`, appVersion)
	header.Set(`DS`, getDS())
	return header
}

func getDS() string {
	r := util.RandString(6)
	t := strconv.Itoa(int(time.Now().Unix()))
	n := `N50pqm7FSy2AkFz2B3TqtuZMJ5TOl3Ep`
	str := `salt=` + n + `&t=` + t + `&r=` + r
	d := []byte(str)
	has := md5.Sum(d)
	md5str := fmt.Sprintf("%x", has)
	return t + `,` + r + `,` + md5str
}

func (m *Mys) getAward() {
	d, err := request.Get(awardUrl, nil)
	if err != nil {
		logger.Logger.Error(`get awards info error`, zap.Error(err))
		return
	}
	err = json.Unmarshal(d, &m.awards)
	if err != nil {
		logger.Logger.Error(`get awards info error`, zap.Error(err))
	} else {
		logger.Logger.Info(`get awards`, zap.Any(`awards`, m.awards))
	}
}

func (m *Mys) getBindingInfo(header http.Header) (*BindingInfo, error) {
	info := &BindingInfo{}
	d, err := request.Get(bindingUrl, header)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(d, &info)
	if err != nil {
		return nil, err
	}
	if info == nil || len(info.Data.List) <= 0 {
		return nil, errors.New(`绑定信息为空`)
	}
	return info, nil
}
